/***************************************************************************//**
 * @file
 * @brief CLI for the Device Table plugin.
 *******************************************************************************
 * # License
 * <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
 *******************************************************************************
 *
 * The licensor of this software is Silicon Laboratories Inc. Your use of this
 * software is governed by the terms of Silicon Labs Master Software License
 * Agreement (MSLA) available at
 * www.silabs.com/about-us/legal/master-software-license-agreement. This
 * software is distributed to you in Source Code format and is governed by the
 * sections of the MSLA applicable to Source Code.
 *
 ******************************************************************************/

#include PLATFORM_HEADER
#ifdef EZSP_HOST
// Includes needed for functions related to the EZSP host
  #include "stack/include/error.h"
  #include "stack/include/ember-types.h"
  #include "app/util/ezsp/ezsp-protocol.h"
  #include "app/util/ezsp/ezsp.h"
  #include "app/util/ezsp/serial-interface.h"
  #include "app/util/zigbee-framework/zigbee-device-common.h"
#else
  #include "stack/include/ember.h"
#endif

#include "hal/hal.h"
#include "app/util/serial/command-interpreter2.h"
#include "af.h"
#include "event_control/event.h"

#include <stdlib.h>
#include "app/framework/plugin/device-table/device-table.h"
#include "app/framework/plugin/device-table/device-table-internal.h"

static EmberNodeId discoverNode;

// --------------------------------
// Print commands
void emAfDeviceTablePrintEUI64(uint8_t *eui64)
{
  uint8_t i;
  for (i = 8; i > 0; i--) {
    emberAfCorePrint("%X", eui64[i - 1]);
  }
}

static void printState(EmberAfPluginDeviceTableDeviceState state)
{
  switch (state) {
    case EMBER_AF_PLUGIN_DEVICE_TABLE_STATE_JOINED:
      emberAfCorePrint(" JOINED");
      break;
    case EMBER_AF_PLUGIN_DEVICE_TABLE_STATE_UNRESPONSIVE:
      emberAfCorePrint(" UNRESPONSIVE");
      break;
    case EMBER_AF_PLUGIN_DEVICE_TABLE_STATE_LEAVE_SENT:
      emberAfCorePrint(" LEAVE_SENT");
      break;
    case EMBER_AF_PLUGIN_DEVICE_TABLE_STATE_LEFT:
      emberAfCorePrint(" LEFT");
      break;
    default:
      break;
  }
}

static void printDeviceId(uint16_t deviceId)
{
  switch (deviceId) {
    case DEVICE_ID_ON_OFF_SWITCH:
      emberAfCorePrint("ON_OFF_SWITCH");
      break;

    case DEVICE_ID_LEVEL_CONTROL_SWITCH:
      emberAfCorePrint("LEVEL_CONTROL_SWITCH");
      break;

    case DEVICE_ID_ON_OFF_OUTPUT:
      emberAfCorePrint("ON_OFF_OUTPUT");
      break;

    case DEVICE_ID_LEVEL_CONTROL_OUTPUT:
      emberAfCorePrint("LEVEL_CONTROL_OUTPUT");
      break;

    case DEVICE_ID_SCENE_SELECTOR:
      emberAfCorePrint("SCENE_SELECTOR");
      break;

    case DEVICE_ID_CONFIG_TOOL:
      emberAfCorePrint("CONFIG_TOOL");
      break;

    case DEVICE_ID_REMOTE_CONTROL:
      emberAfCorePrint("REMOTE_CONTROL");
      break;

    case DEVICE_ID_COMBINED_INTERFACE:
      emberAfCorePrint("COMBINED_INTERFACE");
      break;

    case DEVICE_ID_RANGE_EXTENDER:
      emberAfCorePrint("RANGE_EXTENDER");
      break;

    case DEVICE_ID_MAINS_POWER_OUTLET:
      emberAfCorePrint("MAINS_POWER_OUTLET");
      break;

    case DEVICE_ID_DOOR_LOCK:
      emberAfCorePrint("DOOR_LOCK");
      break;

    case DEVICE_ID_DOOR_LOCK_CONTROLLER:
      emberAfCorePrint("DOOR_LOCK_CONTROLLER");
      break;

    case DEVICE_ID_SIMPLE_SENSOR:
      emberAfCorePrint("SIMPLE_SENSOR");
      break;

    case DEVICE_ID_CONSUMPTION_AWARENESS_DEVICE:
      emberAfCorePrint("CONSUMPTION_AWARENESS_DEVICE");
      break;

    case DEVICE_ID_HOME_GATEWAY:
      emberAfCorePrint("HOME_GATEWAY");
      break;

    case DEVICE_ID_SMART_PLUG:
      emberAfCorePrint("SMART_PLUG");
      break;

    case DEVICE_ID_WHITE_GOODS:
      emberAfCorePrint("WHITE_GOODS");
      break;

    case DEVICE_ID_METER_INTERFACE:
      emberAfCorePrint("METER_INTERFACE");
      break;

    case DEVICE_ID_ON_OFF_LIGHT:
      emberAfCorePrint("ON_OFF_LIGHT");
      break;

    case DEVICE_ID_DIMMABLE_LIGHT:
      emberAfCorePrint("DIMMABLE_LIGHT");
      break;

    case DEVICE_ID_COLOR_DIMMABLE_LIGHT:
      emberAfCorePrint("COLOR_DIMMABLE_LIGHT");
      break;

    case DEVICE_ID_ON_OFF_LIGHT_SWITCH:
      emberAfCorePrint("ON_OFF_LIGHT_SWITCH");
      break;

    case DEVICE_ID_DIMMER_SWITCH:
      emberAfCorePrint("DIMMER_SWITCH");
      break;

    case DEVICE_ID_COLOR_DIMMER_SWITCH:
      emberAfCorePrint("COLOR_DIMMER_SWITCH");
      break;

    case DEVICE_ID_LIGHT_SENSOR:
      emberAfCorePrint("LIGHT_SENSOR");
      break;

    case DEVICE_ID_OCCUPANCY_SENSOR:
      emberAfCorePrint("OCCUPANCY_SENSOR");
      break;

    case DEVICE_ID_SHADE:
      emberAfCorePrint("SHADE");
      break;

    case DEVICE_ID_SHADE_CONTROLLER:
      emberAfCorePrint("SHADE_CONTROLLER");
      break;

    case DEVICE_ID_WINDOW_COVERING_DEVICE:
      emberAfCorePrint("WINDOW_COVERING_DEVICE");
      break;

    case DEVICE_ID_WINDOW_COVERING_CONTROLLER:
      emberAfCorePrint("WINDOW_COVERING_CONTROLLER");
      break;

    case DEVICE_ID_HEATING_COOLING_UNIT:
      emberAfCorePrint("HEATING_COOLING_UNIT");
      break;

    case DEVICE_ID_THERMOSTAT:
      emberAfCorePrint("THERMOSTAT");
      break;

    case DEVICE_ID_TEMPERATURE_SENSOR:
      emberAfCorePrint("TEMPERATURE_SENSOR");
      break;

    case DEVICE_ID_PUMP:
      emberAfCorePrint("PUMP");
      break;

    case DEVICE_ID_PUMP_CONTROLLER:
      emberAfCorePrint("PUMP_CONTROLLER");
      break;

    case DEVICE_ID_PRESSURE_SENSOR:
      emberAfCorePrint("PRESSURE_SENSOR");
      break;

    case DEVICE_ID_FLOW_SENSOR:
      emberAfCorePrint("FLOW_SENSOR");
      break;

    case DEVICE_ID_MINI_SPLIT_AC:
      emberAfCorePrint("MINI_SPLIT_AC");
      break;

    case DEVICE_ID_IAS_CIE:
      emberAfCorePrint("IAS_CIE");
      break;

    case DEVICE_ID_IAS_ANCILLARY_CONTROL:
      emberAfCorePrint("IAS_ANCILLARY_CONTROL");
      break;

    case DEVICE_ID_IAS_ZONE:
      emberAfCorePrint("IAS_ZONE");
      break;

    case DEVICE_ID_IAS_WARNING:
      emberAfCorePrint("IAS_WARNING");
      break;

    default:
      break;
  }
}

void emberAfDeviceTablePrintDeviceTable(void)
{
  uint16_t totalDevices = 0;
  EmberAfPluginDeviceTableEntry *deviceTable = emberAfDeviceTablePointer();
  uint16_t index;

  for (index = 0;
       index < EMBER_AF_PLUGIN_DEVICE_TABLE_DEVICE_TABLE_SIZE;
       index++) {
    if (deviceTable[index].nodeId != EMBER_AF_PLUGIN_DEVICE_TABLE_NULL_NODE_ID) {
      emberAfCorePrint("%d %2x:  ", totalDevices, deviceTable[index].nodeId);
      emAfDeviceTablePrintEUI64(deviceTable[index].eui64);
      emberAfCorePrint(" %d ", deviceTable[index].endpoint);
      printDeviceId(deviceTable[index].deviceId);
      printState(deviceTable[index].state);
      emberAfCorePrintln(" %l", emberAfDeviceTableTimeSinceLastMessage(index));
      totalDevices++;
    }
  }
  emberAfCorePrintln("Total Devices %d", totalDevices);
}

void emAfDeviceTablePrintBuffer(uint8_t *buffer, uint16_t bufLen)
{
  int i;
  for (i = 0; i < bufLen; i++) {
    emberAfCorePrint("%x ", buffer[i]);
  }
  emberAfCorePrintln("");
}

// --------------------------------
// CLI section
void deviceTabeCliServiceDiscoveryCallback(const EmberAfServiceDiscoveryResult* result)
{
  if (result->zdoRequestClusterId == IEEE_ADDRESS_REQUEST) {
    if (result->status == EMBER_AF_UNICAST_SERVICE_DISCOVERY_TIMEOUT) {
      emberAfCorePrintln("Unicast Timeout");
      return;
    }

    uint8_t* eui64ptr = (uint8_t*)(result->responseData);
    emberAfDeviceTableNewDeviceJoinHandler(discoverNode, eui64ptr);
  }
}

void emAfDeviceTableIndexRemoveCommand(void)
{
  uint16_t index = (uint8_t)emberUnsignedCommandArgument(0);
  emAfPluginDeviceTableDeleteEntry(index);
}

void emAfDeviceTableRemoveCommand(void)
{
  EmberEUI64 eui64;
  uint16_t index;

  emberCopyBigEndianEui64Argument(0, eui64);
  index = emberAfDeviceTableGetFirstIndexFromEui64(eui64);

  while (index != EMBER_AF_PLUGIN_DEVICE_TABLE_NULL_INDEX) {
    emAfPluginDeviceTableDeleteEntry(index);
    index = emAfDeviceTableFindNextEndpoint(index);
  }
}

void emAfDeviceTableIndexRouteRepairCommand(void)
{
  uint16_t index = (uint8_t)emberUnsignedCommandArgument(0);
  uint16_t nodeId = emberAfDeviceTableGetNodeIdFromIndex(index);

  if (nodeId != EMBER_AF_PLUGIN_DEVICE_TABLE_NULL_NODE_ID) {
    emberAfCorePrintln("ROUTE REPAIR:  CLI");
    emberAfPluginDeviceTableInitiateRouteRepair(nodeId);
  }
}

void emAfDeviceTableRouteRepairCommand(void)
{
  uint16_t nodeId;
  EmberEUI64 eui64;

  emberCopyBigEndianEui64Argument(0, eui64);
  nodeId = emberAfDeviceTableGetNodeIdFromEui64(eui64);

  if (nodeId != EMBER_AF_PLUGIN_DEVICE_TABLE_NULL_NODE_ID) {
    emberAfCorePrintln("ROUTE REPAIR:  CLI");
    emberAfPluginDeviceTableInitiateRouteRepair(nodeId);
  }
}

void emAfDeviceTableDiscoverPresentNodeCommand(void)
{
  discoverNode = (EmberNodeId)emberUnsignedCommandArgument(0);
  emberAfFindIeeeAddress(discoverNode,
                         deviceTabeCliServiceDiscoveryCallback);
}

void emAfDeviceTableIndexSendCommand(void)
{
  uint16_t index = (uint16_t)emberUnsignedCommandArgument(0);
  emberAfDeviceTableCliIndexSend(index);
}

void emAfDeviceTableSendCommand(void)
{
  uint8_t endpoint = (uint8_t)emberUnsignedCommandArgument(1);
  EmberEUI64 eui64;

  emberCopyBigEndianEui64Argument(0, eui64);
  emberAfDeviceTableSend(eui64, endpoint);
}

void emAfDeviceTableSaveCommand(void)
{
  emAfDeviceTableSave();
}

void emAfDeviceTableLoadCommand(void)
{
  emAfDeviceTableLoad();
}

void emAfPluginDeviceTableSendLeaveCommand(void)
{
  EmberEUI64 eui64;
  emberCopyBigEndianEui64Argument(0, eui64);
}
